#include <iostream>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

using namespace std;

/*
 * 附录 A：循环冗余校验（CRC）算法
 * CRC 校验（Cyclic Redundancy Check）是一种数据传输错误检查方法， CRC 码两个字
 * 节，包含一 16 位的二进制值。它由传输设备计算后加入到数据包中。接收设备重新计算收
 * 到消息的 CRC，并与接收到的 CRC 域中的值比较，如果两值不同，则有误。
 * 具体算法如下：
 * CRC 是先调入一值是全“1”的 16 位寄存器，然后调用一过程将消息中连续的 8 位字
 * 节各当前寄存器中的值进行处理。仅每个字符中的 8Bit 数据对 CRC 有效，起始位和停止位
 * 以及奇偶校验位均无效。
 * CRC 校验字节的生成步骤如下：
 * ① 装一个 16 位寄存器，所有数位均为 1。
 * ② 取被校验串的一个字节与 16 位寄存器的高位字节进行“异或”运算。运算结果放
 * 入这个 16 位寄存器。
 * ③ 把这个 16 寄存器向右移一位。
 * ④ 若向右（标记位）移出的数位是 1，则生成多项式 1010 0000 0000 0001 和这个寄
 * 存器进行“异或”运算；若向右移出的数位是 0，则返回③。
 * ⑤ 重复③和④，直至移出 8 位。
 * ⑥ 取被校验串的下一个字节
 * ⑦ 重复③~⑥，直至被校验串的所有字节均与 16 位寄存器进行“异或”运算，并移位
 * 8 次。
 * ⑧ 这个 16 位寄存器的内容即 2 字节 CRC 错误校验码。
 * 校验码按照先高字节后低字节的顺序存放。
 *
 *
 *
 * */
unsigned short crc16(const unsigned char *ptr, int len)        // ptr 为数据指针，len 为数据长度 
{ 

	unsigned int i;
	unsigned short j,tmp,CRC16;

	CRC16=0xffff;
	for ( i = 0; i < len; i++ )
	{  
	    CRC16 = *ptr ^ CRC16;
	    for ( j = 0; j < 8; j++ )
	    {
			tmp=CRC16 & 0x0001;
			CRC16 =CRC16 >>1;
			if (tmp)
				CRC16=CRC16 ^ 0xa001;    
		}
		*ptr++;
	}	

	return(CRC16);
}

unsigned short checkFile( const string &file)
{
	struct stat stFileStat;
	
	int ret =  stat(file.c_str(), &stFileStat);
	if ( ret < 0 )
	{
		perror("stat error");
		return false;
	}

	cout<<"file size = "<<stFileStat.st_size<<endl;

	char *p = new char [stFileStat.st_size+2];
	
	FILE *fp = fopen(file.c_str(), "r");
	if ( fp == NULL )
	{
		perror("fopen file error");
		return -1;
	}
	
	ret = fread(p,  stFileStat.st_size, 1, fp);
	if (ret != 1)
	{
		perror("fread error");
		return -1;
	}
	
	fclose(fp);
	unsigned short crc16Data = crc16((unsigned char *)p, stFileStat.st_size);
	printf("file crc16 = 0x%04x\n", crc16Data);
	
	delete [] p;

	return crc16Data;
}

int main(int argc, char **argv)
{
	if ( argc <= 1 )
	{
		printf("Uage: %s <file>", argv[0]);
		return -1;
	}
	
	string file=argv[1];
	if (file == "-c")
	{
		return checkFile(argv[2]);
	}
	
	struct stat stFileStat;
	
	int ret =  stat(file.c_str(), &stFileStat);
	if ( ret < 0 )
	{
		perror("stat error");
		return false;
	}

	cout<<"file size = "<<stFileStat.st_size<<endl;

	char *p = new char [stFileStat.st_size+2];
	
	FILE *fp = fopen(file.c_str(), "r");
	if ( fp == NULL )
	{
		perror("fopen file error");
		return -1;
	}
	
	ret = fread(p,  stFileStat.st_size, 1, fp);
	if (ret != 1)
	{
		perror("fread error");
		return -1;
	}
	
	fclose(fp);
	unsigned short crc16Data = crc16((unsigned char *)p, stFileStat.st_size);
	printf("file crc16 = 0x%04x\n", crc16Data);
	
	p[stFileStat.st_size] = crc16Data & 0xff;
	p[stFileStat.st_size+1] = (crc16Data >> 8) & 0xff;
	
	crc16Data = crc16((unsigned char *)p, stFileStat.st_size+2);
	
	if ( crc16Data != 0)
		cout<<"check file crc16 fault! please add crc16 later!"<<endl;
	
	file += ".crc16";
	fp = fopen(file.c_str(), "w");
	if ( fp == NULL )
	{
		perror("fopen file error");
		return -1;
	}
	
	ret = fwrite(p,  stFileStat.st_size+2, 1, fp);
	if (ret != 1)
	{
		perror("fwrite error");
		return -1;
	}
	
	fclose(fp);
	
	delete [] p;
	
	cout<<"add  crc16 ok! "<<endl;

	
}